home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 003 / ff / ff.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  29KB  |  1,101 lines

  1. /*COPYRIGHT (c) 1985 Wang Institute, Tyngsboro, MA 01879 USA */
  2. /*DISCLAIMER:    No guarantees of performance are made. */
  3. /*MODULE    ff: "fast formatter" "Sun 01 Sep 1985" */
  4. /*PGMR      ff: "Gary Perlman" "Wang Institute, Tyngsboro, MA 01879 USA" */
  5. /*VER       $Header: ff.c,v 1.3 85/09/01 21:23:38 perlman Exp $ */
  6. /*COMP      ff: see makefile */
  7.  
  8. /*MANUAL.TH FF 1 "August 10, 1985" "Wang Institute" "UNIX User's Manual"
  9. .\" $Compile: iroff -man.new %f
  10. .SH NAME
  11. ff \- fast text formatter
  12. .SH USAGE
  13. .B ff
  14. [options] [-] [files]
  15. .SH DESCRIPTION
  16. .I ff
  17. is a simple text formatter for flexible uniform formatting of
  18. input files.
  19. Program options are used to control formatting.
  20. This is in contrast to text formatters like
  21. .I nroff (1)
  22. that require special format requests to be part of their input files.
  23. Besides avoiding cryptic format requests in text,
  24. .I ff
  25. is considerably faster than traditional formatters like
  26. .I nroff (1)
  27. and even simple formatters like
  28. .I fmt (1).
  29. .PP
  30. The most complicated concept with
  31. .I ff
  32. is that of a line break.
  33. A line break causes an interruption in the filling
  34. (evening out of the text lines).
  35. Line breaks occur when special characters are seen at the beginnings
  36. of lines, or when all lines are broken.
  37. By default, any non-alphanumeric character will cause a break,
  38. but this can be controlled with the
  39. .B -B
  40. option.
  41. A blank line always causes a break.
  42. */
  43.  
  44.  
  45. #include <stdio.h>
  46. #include <ctype.h>
  47. #ifdef BSD
  48. #include <strings.h>
  49. #define strchr index
  50. #else
  51. #ifndef AMIGA
  52. #include <string.h>
  53. #endif
  54. #endif
  55.  
  56. #ifdef AMIGA        /* min/max used as variables */
  57. #undef min
  58. #undef max
  59. #endif
  60.  
  61. typedef    int     Status;
  62. #define    SUCCESS   ((Status) 0)
  63. #define    FAILURE   ((Status) 1)
  64. typedef    int     Boole;
  65. #define    TRUE      ((Boole) 1)
  66. #define    FALSE     ((Boole) 0)
  67.  
  68. #define    TAB       '\t'
  69. #define    EOL       '\n'
  70. #define    FF        '\f'
  71. #define    EOS       '\0'
  72. #define    SP        ' '
  73. #define    MAXLEN    128                  /* max length of lines */
  74. #define    MAXLINES  200                  /* max # lines on pages */
  75.  
  76. /* Alphabetical listing of this file's functions */
  77. void    beginline ();  /* process text at the beginning of lines */
  78. void    beginpage ();  /* handle pagination at page breaks */
  79. Status    dobreak ();    /* handle broken lines, if appropriate */
  80. void    dofill ();     /* do the text filling */
  81. char    *dotab ();     /* return expanded tabs in line */
  82. void    endpage ();    /* handle pagination at page ends */
  83. char    *expand ();    /* expand strings in three part titles */
  84. Status    ff ();         /* main formatting routine */
  85. int     initial ();    /* set options and check consistency */
  86. char    *itoa ();      /* convert integer to ascii format, with padding */
  87. void    dojustify ();  /* even out (justify) the right margin of filled lines */
  88. char    *preprocess ();/* handle blank trimming and titling */
  89. void    println ();    /* print a line, watching for page boundaries */
  90. void    repeat ();     /* repeatedly print a character */
  91. Status    setint ();     /* check type & convert a string to an integer */
  92. char    *threepart (); /* build three part titles */
  93. void    usage ();      /* print usage summary */
  94.  
  95. /* GLOBALS */
  96. char    *Argv0;              /* will be name of program */
  97. int     Curpos;              /* current position on output line */
  98. char    *Filename;           /* current input file name */
  99. Boole    Filling;             /* is text being filled right now */
  100. char    Justbuf[MAXLEN];     /* buffer for justified text */
  101. int     Justpos;             /* current position in justification buffer */
  102. int     Outline;             /* output line number */
  103. int     Pageline;            /* line number on current output page */
  104. int     Pagenum;             /* current page number */
  105.  
  106. /* Default values of options */
  107. #define    MAXTAB     20                  /* max # of tab stops */
  108. #define    FOOTSIZE    5                  /* default footer size */
  109. #define    HEADSIZE    5                  /* default header size */
  110. #define    NUMWIDTH    4                  /* default width of line numbers */
  111. #define    PAGESIZE   66                  /* default length of page */
  112. #define    WIDTH      72                  /* default width of page */
  113. #define    PAGENUM   '%'                  /* expands to page number in titles */
  114. #define    FILENAME  '*'                  /* expands to file name in title */
  115. #define    HEADER      "|File: *||Page: %|" /* default page header */
  116.  
  117. /*MANUAL.SH OPTIONS
  118. There are many, many options to allow control of
  119. indentation, line width, line spacing, filling,
  120. pagination with headers and footers,
  121. line numbering, right justification,
  122. and perhaps some other things.
  123. They have extensive type and range checking
  124. that produces diagnostic error messages,
  125. so warnings of obviously wrong options will not be discussed here.
  126. In general, options that imply the use of others
  127. work the way they should; if the page size is set,
  128. then pagination is automatically assumed.
  129. Some combinations of options give impressive, even artistic, effects.
  130. Making a small text file and playing with it is the easiest
  131. way to learn how the options interact.
  132. .de OP
  133. .TP
  134. .B -\\$1 \\$2
  135. ..
  136. */
  137.  
  138.     Boole    Breaklines = FALSE; /*MANUAL.OP b
  139. Break all lines of text.
  140. That is, don't even-out lines by filling.
  141. By default, text lines are filled.
  142. */
  143.  
  144.     char    *Breakchars = NULL; /*MANUAL.OP B breakchars
  145. Change the set of characters that cause line breaks at the start of lines to
  146. .I breakchars.
  147. By default, any characters but letters and numbers cause a break.
  148. A good choice for break characters might be "*-+" and TABS
  149. that might be used for lists.
  150. */
  151.  
  152.     Boole    Center = FALSE; /*MANUAL.OP c
  153. Center all lines of text.
  154. This option stops all filling of text.
  155. */
  156.  
  157.     Boole    Delspace = FALSE; /*MANUAL.OP d
  158. Delete white space at the beginning and end of lines.
  159. This option is useful to help un-format text to be re-formatted.
  160. */
  161.  
  162.     Boole    Delline = FALSE; /*MANUAL.OP D
  163. Delete empty input lines.
  164. An input line is empty if it has no characters,
  165. not even invisible character like tabs or spaces.
  166. This option can be combined with the option to remove white space
  167. to delete visibly blank lines.
  168. */
  169.  
  170.     char    *Footer = NULL; /*MANUAL.OP f footer
  171. Set the page footer to the string
  172. .I footer.
  173. This can be any string,
  174. but if the first character is not a letter or a digit,
  175. but a punctuation character like /,
  176. then that character separates the left,
  177. center, and right fields of a title.
  178. For example, the title
  179. .ce
  180. "/ff: fast formatter//1985/"
  181. would have "ff: fast formatter" as a left justified field
  182. and 1985 as a right justified field on each page.
  183. Note that there is no middle field in this example,
  184. but there could have been, between the two consecutive /'s.
  185. There are two special characters, % and *,
  186. that respectively are variables for the page number
  187. and the input file name.
  188. The default page footer is blank.
  189. */
  190.  
  191.     int     Footsize = FOOTSIZE; /*MANUAL.OP F footersize
  192. Set the number of blank lines at the bottom of the page.
  193. The footer, if any, is placed in the middle of the space,
  194. which by default, is five lines.
  195. If
  196. .I footersize
  197. is 0, no footer will be printed.
  198. */
  199.  
  200.     char    *Header = HEADER; /*MANUAL.OP h header
  201. Set the page header.
  202. See the description of three-part titles for the
  203. .B -f footer
  204. option.
  205. The default page header is
  206. .ce
  207. "|File: *||Page: %|".
  208. */
  209.  
  210.     int     Headsize = HEADSIZE; /*MANUAL.OP H headersize
  211. See the description of the footer size.
  212. */
  213.  
  214.     int     Indent = 0; /*MANUAL.OP i indent
  215. Set the indentation of the text to
  216. .I indent
  217. spaces.
  218. Note that this effectively reduces the usable width of the page.
  219. */
  220.  
  221.     int     Tindent = 0; /*MANUAL.OP I tempindent
  222. Set the temporary indent.
  223. This causes filled text found immediately after a break to
  224. be indented for one line.
  225. It is useful for indenting the first lines of paragraphs.
  226. If
  227. .I tempindent
  228. is negative,
  229. the the temporary indent will be relative to the current
  230. .I indent
  231. value.
  232. If the
  233. .I tempindent
  234. value is more negative than the
  235. .I indent
  236. value is positive,
  237. .I ff
  238. will automatically increase
  239. .I indent.
  240. */
  241.  
  242.     Boole    Justify = FALSE; /*MANUAL.OP j
  243. Justify the text.
  244. That is, even the right margin by inserting spaces in the line.
  245. Only filled text can be justified.
  246. */
  247.  
  248.     Boole    Numlines = FALSE; /*MANUAL.OP n
  249. Number all output lines produced by the input text.
  250. Lines from multiple line spacing or pagination will not
  251. be numbered.
  252. */
  253.  
  254.     int     Numwidth = NUMWIDTH; /*MANUAL.OP N numberwidth
  255. Set the width of the line numbers.
  256. The default is to take up 4 spaces.
  257. Note that this effectively reduces the usable part of the page.
  258. */
  259.  
  260.     Boole    Paginate = FALSE; /*MANUAL.OP p
  261. Paginate the output.
  262. See the options for page header and footer control.
  263. */
  264.  
  265.     int     Pagesize = PAGESIZE; /*MANUAL.OP P pagesize
  266. Set the number of lines in a page to
  267. .I pagesize.
  268. By default, the standard 66 line page is assumed.
  269. */
  270.  
  271.     int     Spacing = 1; /*MANUAL.OP s spacing
  272. Set the line spacing.
  273. By default, text is single spaced (\fIspacing\fR equals 1).
  274. */
  275.  
  276.     int     Tab[MAXTAB];
  277.     int     Ntabs = 0; /*MANUAL.OP t tab
  278. Set individual absolute and relative tab stops.
  279. These values tell the formatter
  280. where to put the text (from an unfilled line)
  281. that follows a tab character.
  282. Each tab stop is supplied with its own
  283. .B -t
  284. option; there is no way to bundle them.
  285. .I tab
  286. values can be integers without a plus sign.
  287. These are \fIabsolute\fR tab settings;
  288. the tabs go to that position.
  289. The values must increase monotonically.
  290. If a
  291. .I tab
  292. value is preceded by a plus sign,
  293. then it is interpreted \fIrelative\fR to the previous tab setting.
  294. For example, a tab setting of 40 followed by one of +20
  295. will set the second tab stop to 60.
  296. */
  297.  
  298.     int     Alltabs = 0; /*MANUAL.OP T tabs
  299. Set tab stops to every
  300. .I tabs
  301. spaces.
  302. It is useful to get equally spaced tabs.
  303. This option cannot be used with the other tab setting option.
  304. */
  305.  
  306.     Boole    Uppercase = FALSE; /*MANUAL.OP u
  307. Print All Input Text As Initial Upper-Case Titles,
  308. Like This Sentence.
  309. This option goes well with the one for centering lines.
  310. */
  311.  
  312.     /*MANUAL.OP U
  313. Print a usage summary of all the options and stop.
  314. */
  315.  
  316.     int     Width = WIDTH; /*MANUAL.OP w width
  317. Set the page width.
  318. By default, the page width is 72 characters.
  319. Note that the usable line length is sometimes less
  320. than the page width.
  321. If line numbering or indentation is requested,
  322. these subtract from the line length.
  323. */
  324.  
  325. /*MANUAL.SH EXAMPLES
  326. Some of these examples can make shell scripts or aliases.
  327. .nf
  328. .ta .5i
  329. .sp
  330. Centered Titles: title
  331.     ff -dcu $*
  332. Double Spaced Unfilled Paginated indented (for editing): draft
  333.     ff -s 2 -b -p -f "`date`" -i 8 $*
  334. Program Listing: cpr
  335.     H="@        Dir: `pwd`@@File: *@"
  336.     F="@        $NAME@`date`@Page %@"
  337.     ff -b -N 8 -H 3 -h "$H" -F 3 -f "$F" -T 4 -w 79 -i 2 $*
  338. Reformat Paragraphed Text: nr
  339.     ff -jd -I 5 -i 10 -w 65 -B "TAB SP'*.@" $*
  340. .fi
  341. */
  342. /*MANUAL.SH DIAGNOSTICS
  343. Some options are incompatible with others.
  344. For example, centered text cannot be right-justified.
  345. .I ff
  346. will not allow inconsistent combinations of options.
  347. */
  348. /*MANUAL.SH "SEE ALSO"
  349. fmt(1), nroff(1), scribe(1w)
  350. */
  351. /*MANUAL.SH AUTHOR
  352. Gary Perlman (with help from many students)
  353. */
  354. /*MANUAL.SH STATUS
  355. Pretty well tested.
  356. */
  357.  
  358. /* Macro Functions */
  359. /*MACRO isendsent: is the character one that ends a sentence? */
  360. #define    isendsent(c) ((c) == '.' || (c) == '?' || (c) == '!')
  361.  
  362. /*MACRO justchar: add char to a buffer that will later be flushed */
  363. #define    justchar(c) (Justbuf[Justpos++] = (c))
  364.  
  365. /*MACRO fillchar: save to justify if necessary, else output */
  366. #define fillchar(c) \
  367.     { \
  368.     if (Justify == TRUE) justchar (c); \
  369.     else putc (c, stdout); \
  370.     }
  371.  
  372. /*FUNCTION main: loop through files in classic UNIX filter style */
  373. main (argc, argv)
  374. int     argc;     /* argument count */
  375. char    **argv;   /* argument vector */
  376.     {
  377.     Status     ff ();      /* ff (file, ioptr) will filter files */
  378.     Status    status;     /* return status of filter () */
  379.     int     firstfile;  /* first file name index returned by initial */
  380.  
  381.     firstfile = initial (argc, argv);
  382.     status = filter (argc, argv, firstfile, ff);
  383.     exit (status);
  384.     }
  385.  
  386. /*FUNCTION setint: check type, convert string, and set integer option */
  387. Status
  388. setint (flag, value, var, min, max)
  389. int     flag;    /* the single character option name */
  390. char    *value;  /* the candidate value, in string format */
  391. int     *var;    /* ptr to variable to stuff in answer */
  392. int     min;     /* minimum allowed value */
  393. int     max;     /* maximum allowed value */
  394.     {
  395.     int     tmpvar;
  396.     if (number (value) == 1) /* number returns 1 for integers, 2 for reals */
  397.         {
  398.         tmpvar = atoi (value);
  399.         if (tmpvar >= min && tmpvar <= max)
  400.             {
  401.             *var = tmpvar;
  402.             return (SUCCESS);
  403.             }
  404.         fprintf (stderr, "%s: -%c option value must be between %d and %d\n",
  405.             Argv0, flag, min, max);
  406.         return (FAILURE);
  407.         }
  408.     fprintf (stderr, "%s: -%c option requires an integer value\n", Argv0, flag);
  409.     return (FAILURE);
  410.     }
  411.  
  412. /*FUNCTION usage: print help menu */
  413. void
  414. usage (ioptr)
  415. FILE    *ioptr;
  416.     {
  417.     fprintf (ioptr, "%s: fast text formatter options:\n", Argv0);
  418.     fprintf (ioptr, "-b      break all lines of text--do no filling\n");
  419.     fprintf (ioptr, "-B s    line break characters\n");
  420.     fprintf (ioptr, "-c      center all text lines\n");
  421.     fprintf (ioptr, "-d      delete blank space around input lines\n");
  422.     fprintf (ioptr, "-D      delete blank input lines\n");
  423.     fprintf (ioptr, "-f s    page footer three-part title\n");
  424.     fprintf (ioptr, "-F i    page footer size (%d lines)\n", Footsize);
  425.     fprintf (ioptr, "-h s    page header three-part title (%s)\n", Header);
  426.     fprintf (ioptr, "-H i    page header size (%d lines)\n", Headsize);
  427.     fprintf (ioptr, "-i i    text indentation (%d spaces)\n", Indent);
  428.     fprintf (ioptr, "-I i    indent after line breaks (%d spaces)\n", Tindent);
  429.     fprintf (ioptr, "-j      justify the right margin of the text\n");
  430.     fprintf (ioptr, "-n      number output text lines\n");
  431.     fprintf (ioptr, "-N i    width of line numbers (%d spaces)\n", Numwidth);
  432.     fprintf (ioptr, "-p      paginate output\n");
  433.     fprintf (ioptr, "-P i    page size (%d lines)\n", Pagesize);
  434.     fprintf (ioptr, "-s i    line spacing (%d line)\n", Spacing);
  435.     fprintf (ioptr, "-t i    absolute or relative tab stop\n");
  436.     fprintf (ioptr, "-T i    uniform tab stops\n");
  437.     fprintf (ioptr, "-u      show text with upper-case initial letters\n");
  438.     fprintf (ioptr, "-U      print long usage synopsis for this command\n");
  439.     fprintf (ioptr, "-w i    page line width (%d spaces)\n", Width);
  440.     }
  441.  
  442. /*FUNCTION initial: set options */
  443. int
  444. initial (argc, argv) char **argv;
  445.     {
  446.     extern    char    *optarg;   /* string value to option set by getopt */
  447.     extern    int     optind;    /* will be index of first command operand */
  448.     int     errcount = 0;        /* count of number of errors */
  449.     int     flag;              /* options flag names read in here */
  450.     char    *optstring =       /* Boolean flags and integer options with : */
  451.         "bcdDjnpuUB:f:F:h:H:i:I:N:P:s:t:T:w:";
  452.  
  453.     Argv0 = argv[0];
  454.     while ((flag = getopt (argc, argv, optstring)) != EOF)
  455.         switch (flag)
  456.         {
  457.         default:
  458.             errcount++;
  459.             break;
  460.  
  461.         case 'b':
  462.             Breaklines = TRUE;
  463.             break;
  464.  
  465.         case 'B':
  466.             Breakchars = optarg;
  467.             break;
  468.  
  469.         case 'c':
  470.             Center = TRUE;
  471.             Breaklines = TRUE;
  472.             break;
  473.  
  474.         case 'd':
  475.             Delspace = TRUE;
  476.             break;
  477.  
  478.         case 'D':
  479.             Delline = TRUE;
  480.             break;
  481.  
  482.         case 'f':
  483.             Footer = optarg;
  484.             Paginate = TRUE;
  485.             break;
  486.  
  487.         case 'F':
  488.             if (setint (flag, optarg, &Footsize, 0, MAXLINES) == FAILURE)
  489.                 errcount++;
  490.             Paginate = TRUE;
  491.             break;
  492.  
  493.         case 'h':
  494.             Header = optarg;
  495.             Paginate = TRUE;
  496.             break;
  497.  
  498.         case 'H':
  499.             if (setint (flag, optarg, &Headsize, 0, MAXLINES) == FAILURE)
  500.                 errcount++;
  501.             Paginate = TRUE;
  502.             break;
  503.  
  504.         case 'i':
  505.             if (setint (flag, optarg, &Indent, 0, MAXLEN) == FAILURE)
  506.                 errcount++;
  507.             break;
  508.  
  509.         case 'I':
  510.             if (setint (flag, optarg, &Tindent, -MAXLEN, MAXLEN) == FAILURE)
  511.                 errcount++;
  512.             break;
  513.  
  514.         case 'j':
  515.             Justify = TRUE;
  516.             break;
  517.  
  518.         case 'N':
  519.             if (setint (flag, optarg, &Numwidth, 1, MAXLEN) == FAILURE)
  520.                 errcount++;
  521.             /* FALLTHROUGH */
  522.  
  523.         case 'n':
  524.             Numlines = TRUE;
  525.             break;
  526.  
  527.         case 'P':
  528.             if (setint (flag, optarg, &Pagesize, 1, MAXLINES) == FAILURE)
  529.                 errcount++;
  530.             /* FALLTHROUGH */
  531.  
  532.         case 'p':
  533.             Paginate = TRUE;
  534.             break;
  535.  
  536.         case 's':
  537.             if (setint (flag, optarg, &Spacing, 1, MAXLINES) == FAILURE)
  538.                 errcount++;
  539.             break;
  540.  
  541.         case 't':
  542.             if (Ntabs >= MAXTAB)
  543.                 {
  544.                 fprintf (stderr, "%s: at most %d -%c options allowed\n",
  545.                     Argv0, MAXTAB, flag);
  546.                 errcount++;
  547.                 }
  548.             else if (setint (flag, optarg, &Tab[Ntabs], 0, MAXLEN) == FAILURE)
  549.                 errcount++;
  550.             else if (Ntabs > 0)
  551.                 {
  552.                 if (*optarg == '+')
  553.                     Tab[Ntabs] += Tab[Ntabs-1];
  554.                 else if (Tab[Ntabs] <= Tab[Ntabs-1])
  555.                     {
  556.                     fprintf (stderr, "%s: -%c values must increase\n",
  557.                         Argv0, flag);
  558.                     errcount++;
  559.                     }
  560.                 }
  561.             if (Tab[Ntabs] >= MAXLEN)
  562.                 {
  563.                 fprintf (stderr, "%s: -%c values must be < %d\n",
  564.                     Argv0, flag, MAXLEN);
  565.                 errcount++;
  566.                 }
  567.             Ntabs++;
  568.             break;
  569.  
  570.         case 'T':
  571.             if (setint (flag, optarg, &Alltabs, 1, MAXLEN) == FAILURE)
  572.                 errcount++;
  573.             break;
  574.  
  575.         case 'u':
  576.             Uppercase = TRUE;
  577.             break;
  578.  
  579.         case 'U':
  580.             usage (stdout);
  581.             exit (0);
  582.  
  583.         case 'w':
  584.             if (setint (flag, optarg, &Width, 1, MAXLEN) == FAILURE)
  585.                 errcount++;
  586.             break;
  587.         }
  588.  
  589.     /* Now check validity of option settings */
  590.     if (Tindent < 0 && Indent < (-Tindent))
  591.         Indent = (-Tindent);
  592.     if (Ntabs > 0 && Alltabs > 0)
  593.         {
  594.         fprintf (stderr, "%s: can't set individual and all tabs\n", Argv0);
  595.         errcount++;
  596.         }
  597.     if (Center == TRUE && Justify == TRUE)
  598.         {
  599.         fprintf (stderr, "%s: centering and justifying incompatible\n", Argv0);
  600.         errcount++;
  601.         }
  602.     else if (Breaklines == TRUE && Justify == TRUE)
  603.         {
  604.         fprintf (stderr, "%s: breaking and justifying incompatible\n", Argv0);
  605.         errcount++;
  606.         }
  607.     if (Ntabs > 0 && Center == TRUE)
  608.         {
  609.         fprintf (stderr,"%s: centering and setting tabs incompatible\n", Argv0);
  610.         errcount++;
  611.         }
  612.     if ((Ntabs > 0 || Alltabs > 0) && (Justify == TRUE))
  613.         {
  614.         fprintf (stderr, "%s: tabstops and justifying incompatible\n", Argv0);
  615.         errcount++;
  616.         }
  617.  
  618.     /* Print an error message and exit or return index to first file name */
  619.     if (errcount > 0)
  620.         {
  621.         fprintf (stderr, "Usage: %s [options] [-] [files]\n", Argv0);
  622.         exit (FAILURE);
  623.         }
  624.     return (optind);
  625.     }
  626.  
  627. /*FUNCTION repeat: repeat a character some number of times */
  628. void
  629. repeat (c, n)
  630. int     c;     /* character to print */
  631. int     n;     /* number of times to print c */
  632.     {
  633.     while (n-- > 0)
  634.         putc (c, stdout);
  635.     }
  636.  
  637. /*FUNCTION dotab: expand tabs to spaces to tab stops, returns static buffer */
  638. /*ALGORITHM
  639.     if all tabs are set to the same value (Alltabs > 0)
  640.         then advance on a tab to the next tab stop
  641.     else if there are individual tabs set (Ntabs > tabno)
  642.         then advance (possibly retreat!) to next set tab
  643.     else just treat the tab like a space character
  644. */
  645. char *
  646. dotab (line)
  647. register    char    *line;
  648.     {
  649.     static    char outline[MAXLEN];   /* new line will be built here */
  650.     register char *lptr;            /* pointer to current position in outline */
  651.     register char *nextptr;         /* position of next tab stop */
  652.     int     tabno = 0;              /* how many tabs have been processed */
  653.  
  654.     for (lptr = outline; *line != EOS && *line != EOL; line++)
  655.         {
  656.         if (*line == TAB)
  657.             {
  658.             if (Alltabs > 0)
  659.                 nextptr = lptr + Alltabs - ((lptr - outline) % Alltabs);
  660.             else if (Ntabs > tabno) /* move to next set tab */
  661.                 nextptr = outline + Tab[tabno++];
  662.             else
  663.                 nextptr = lptr + 1;
  664.             if (lptr < nextptr)
  665.                 do    {
  666.                     *lptr++ = SP;
  667.                 } while (lptr < nextptr);
  668.             else lptr = nextptr;
  669.             }
  670.         else
  671.             *lptr++ = *line;
  672.  
  673.         /* check for line overflow */
  674.         if (lptr >= (outline + MAXLEN))
  675.             return (NULL);
  676.         }
  677.  
  678.     /* end the expanded tab string and return */
  679.     *lptr = EOS;
  680.     return (outline);
  681.     }
  682.  
  683. /*FUNCTION dojustify: even the right margin for all lines */
  684. /*ALGORITHM
  685.         directly output any indenting spaces (this is not expanded)
  686.         trim trailing spaces (don't want to pad at end)
  687.         count number of spaces inside line (where extra spaces will be inserted)
  688.         distribute the extra spaces needed among the spaces there
  689.     Note: if we are not filling (e.g., last line of output before a break)
  690.         then we do not justfy the right margin.
  691. */
  692. void
  693. dojustify ()
  694.     {
  695.     register char *line;       /* will point to first non-space char on line */
  696.     int     width;             /* width of line to pad - #'s and indent */
  697.     register char *lptr;       /* zips through the line */
  698.     int     pad;               /* will need to pad with this many spaces */
  699.     int     spaces = 0;        /* will be number of embedded spaces in line */
  700.     register int i;            /* used inside inner loop */
  701.     int     n = 0;
  702.  
  703.     if (Justpos == 0)          /* nothing to justify, so bail out */
  704.         return;
  705.  
  706.     /* strip spaces from end of line and end with EOS */
  707.     for (lptr = Justbuf+Justpos; lptr>Justbuf && isspace(lptr[-1]); lptr--);
  708.     *lptr = EOS;
  709.  
  710.     width = Width - (Numlines == TRUE ? Numwidth : 0);
  711.     pad = width - (lptr - Justbuf);
  712.  
  713.     for (line = Justbuf; *line == SP; line++)
  714.         putc (*line, stdout);
  715.  
  716.     if (Filling == TRUE && pad > 0) /* might not fill last line of output */
  717.         for (lptr = line; *lptr != EOS; lptr++)
  718.             if (*lptr == SP)
  719.                 spaces++;
  720.  
  721.     if (spaces > 0) /* we have places to insert spaces */
  722.         {
  723.         for (lptr = line; *lptr != EOS; lptr++)
  724.             {
  725.             if (*lptr == SP)
  726.                 for (i = 0; i < pad; i++)
  727.                     if (++n == spaces)
  728.                         {
  729.                         putc (SP, stdout);
  730.                         n = 0;
  731.                         }
  732.             putc (*lptr, stdout);
  733.             }
  734.         }
  735.     else /* just output the line */
  736.         for (lptr = line; *lptr != EOS; lptr++)
  737.             putc (*lptr, stdout);
  738.  
  739.     Justpos = 0; /* reset buffer position for next line */
  740.     }
  741.  
  742. /*FUNCTION println: print lines while watching for page boundaries */
  743. void
  744. println (count)
  745. int     count;    /* how many lines to print (== Spacing) */
  746.     {
  747.     Curpos = 0;
  748.     while (count-- > 0)
  749.         {
  750.         putc (EOL, stdout);
  751.         Pageline++;
  752.         if (Paginate == TRUE && ((Pagesize - Pageline) == Footsize))
  753.             {
  754.             endpage (TRUE);
  755.             return;
  756.             }
  757.         }
  758.     }
  759.  
  760. /*FUNCTION beginline: do any needed justification, paginate if requested, */
  761. /*  handle line numbering, temp and regular indents based on prev. filling */
  762. void
  763. beginline (filling)
  764. Boole    filling;     /* are we filling now? */
  765.     {
  766.     int     count;
  767.     Boole    newfill = (Filling == FALSE && filling == TRUE);
  768.  
  769.     Filling = filling;
  770.     if (Justify == TRUE)
  771.         dojustify ();
  772.     Outline++;
  773.     Curpos = 0;
  774.  
  775.     if (Paginate == TRUE && Pageline == 0)
  776.         beginpage ();
  777.     else if (Outline > 1)
  778.         println (Spacing);
  779.  
  780.     if (Numlines == TRUE)
  781.         {
  782.         char    *ptr = itoa (Outline, Numwidth - 1);
  783.         Curpos += Numwidth;
  784.         while (*ptr)
  785.             putc (*ptr++, stdout);
  786.         putc (SP, stdout);
  787.         }
  788.  
  789.     count = Indent;
  790.     if (newfill == TRUE)
  791.         count += Tindent;
  792.     Curpos += count;
  793.  
  794.     if (Justify == TRUE)
  795.         while (count-- > 0)
  796.             justchar (SP);
  797.     else
  798.         repeat (SP, count);
  799.     }
  800.  
  801. /*FUNCTION itoa: integer to ascii conversion */
  802. char *
  803. itoa (n, pad)
  804. register int n;    /* the integer to be printed as a string */
  805. int     pad;       /* amount of space to pad number to */
  806.     {
  807.     static char numbuf[MAXLEN]; /* answer built in here */
  808.     register char *nptr;        /* will be pointer to beginning of number */
  809.     Boole    negflg = FALSE;     /* is the number a negative value? */
  810.  
  811.     /* static numbuf is initialized to 0's, so numbuf[MAXLEN-1] == EOS */
  812.     if (n == 0)
  813.         {
  814.         nptr = &numbuf[MAXLEN-1];
  815.         *--nptr = '0';
  816.         }
  817.     else
  818.         {
  819.         if (n < 0)
  820.             {
  821.             n = (-n);
  822.             negflg = TRUE;
  823.             }
  824.         for (nptr = &numbuf[MAXLEN-1]; n != 0; n /= 10)
  825.             *--nptr = (n % 10) + '0';
  826.         if (negflg == TRUE)
  827.             *--nptr = '-';
  828.         }
  829.  
  830.     while (pad > numbuf+MAXLEN-1-nptr)
  831.         *--nptr = SP;
  832.  
  833.     return (nptr);
  834.     }
  835.  
  836. /*FUNCTION expand: insert file/page for characters in field of 3part title */
  837. char *
  838. expand (title, page, file)
  839. register    char    *title;   /* the title to be expanded */
  840. int     page;                 /* the current page number */
  841. char    *file;                /* the name of the current file */
  842.     {
  843.     static     char answer[MAXLEN];  /* title expanded into this buffer */
  844.     register     char *aptr;       /* pointer to answer buf */
  845.     register    char *ptr;        /* generic string handling pointer */
  846.  
  847.     for (aptr = answer; *title != EOS; title++)
  848.         switch (*title)
  849.         {
  850.         default: 
  851.             *aptr++ = *title;
  852.             break;
  853.  
  854.         case PAGENUM:
  855.             ptr = itoa (page, 0);
  856.             while (*ptr != EOS)
  857.                 *aptr++ = *ptr++;
  858.             break;
  859.  
  860.         case FILENAME:
  861.             for (ptr = file; *ptr != EOS; ptr++)
  862.                 *aptr++ = *ptr;
  863.             break;
  864.         }
  865.     *aptr = EOS;
  866.     return (answer);
  867.     }
  868.  
  869. /*FUNCTION threepart: 3-part title with left/center/right justified fields */
  870. /*  any punctuation character can be the title delimiter: see nroff(1) */
  871. char *
  872. threepart (title, page, file, width)
  873. char    *title;   /* the three part title */
  874. int     page;     /* the current page number */
  875. char    *file;    /* the current file name */
  876. int     width;    /* the current page width */
  877.     {
  878.     static char answer[MAXLEN];   /* answer stuffed in here */
  879.     register char *aptr;          /* pointer to answer buffer */
  880.     int     delim;                /* title delimiter character */
  881.  
  882.     title = expand (title, page, file);
  883.     if (!ispunct (*title))
  884.         return (title);
  885.     for (aptr = answer; aptr < answer + width; aptr++)
  886.         *aptr = SP;
  887.     aptr = answer;
  888.     delim = *title++;
  889.     while (*title != EOS && *title != delim)
  890.         *aptr++ = *title++;
  891.     if (*title++ != EOS) /* now do center */
  892.         {
  893.         char    *lptr = title;
  894.         while (*lptr != EOS && *lptr != delim)
  895.             lptr++;
  896.         aptr = answer + (width - (lptr - title)) / 2;
  897.         if (aptr >= answer)
  898.             while (*title != EOS && *title != delim)
  899.                 *aptr++ = *title++;
  900.         else
  901.             while (*title != EOS && *title != delim)
  902.                 title++;
  903.         if (*title++ != EOS) /* now do left part */
  904.             {
  905.             char    *eptr = title;
  906.             while (*eptr != EOS && *eptr != delim)
  907.                 eptr++;
  908.             eptr--;
  909.             aptr = answer + width - 1;
  910.             while (eptr >= title)
  911.                 *aptr-- = *eptr--;
  912.             }
  913.         }
  914.     answer[width] = EOS;
  915.     return (answer);
  916.     }
  917.  
  918. /*FUNCTION beginpage: handle page header */
  919. void
  920. beginpage ()
  921.     {
  922.     Pagenum++;
  923.     if (Paginate == TRUE && Headsize > 0)
  924.         {
  925.         int     space1 = Headsize / 2;
  926.         int     space2 = Headsize - space1;
  927.         char    *optr = threepart (Header, Pagenum, Filename, Width);
  928.  
  929.         repeat (EOL, space1);
  930.         while (*optr != EOS)
  931.             putc (*optr++, stdout);
  932.         repeat (EOL, space2);
  933.         Pageline = Headsize;
  934.         }
  935.     }
  936.  
  937. /*FUNCTION endpage: break filling (output justified text) print footer */
  938. /* begin a new page if there is more */
  939. void
  940. endpage (more)
  941. Boole    more;
  942.     {
  943.     if (Justify == TRUE)
  944.         dojustify ();
  945.  
  946.     if (Paginate == TRUE)
  947.         {
  948.         int     space1 = Footsize / 2;
  949.         int     space2 = Footsize - space1;
  950.         char    *optr = threepart (Footer, Pagenum, Filename, Width);
  951.  
  952.         repeat (EOL, Pagesize - Pageline - space2);
  953.         while (*optr != EOS)
  954.             putc (*optr++, stdout);
  955.         repeat (EOL, space2);
  956.         Pageline = 0;
  957.         }
  958.     else if (Curpos > 0 || Indent > 0 || Numlines == TRUE)
  959.         putc (EOL, stdout);
  960.  
  961.     if (more == TRUE)
  962.         beginpage ();
  963.     }
  964.  
  965. /*FUNCTION preprocess: trim space, find blank lines, do titling */
  966. char *
  967. preprocess (line)
  968. register    char    *line;
  969.     {
  970.     register    char    *lptr;
  971.     if (Paginate == TRUE && *line == FF)
  972.         {
  973.         line++;
  974.         endpage (TRUE);
  975.         }
  976.     if (Delspace == TRUE) /* delete blank space */
  977.         {
  978.         while (isspace (*line))
  979.             line++;
  980.         for (lptr = line; *lptr != EOS; lptr++);
  981.         while (lptr > line && isspace (*(lptr-1)))
  982.             lptr--;
  983.         *lptr = EOS;
  984.         }
  985.     if (Delline == TRUE && (*line == EOL || *line == EOS))
  986.         return (NULL);
  987.     if (Uppercase == TRUE)
  988.         {
  989.         Boole     newword;    /* are we at the start of a new word? */
  990.         for (lptr = line, newword = TRUE; *lptr != EOS; lptr++)
  991.             {
  992.             if (newword && islower (*lptr))
  993.                 *lptr = toupper (*lptr);
  994.             newword = !isalnum (*lptr);
  995.             }
  996.         }
  997.     return (line);
  998.     }
  999.  
  1000. /*FUNCTION dobreak: process broken line & return success status */
  1001. Status
  1002. dobreak (lptr)
  1003. register char *lptr;    /* line to print out */
  1004.     {
  1005.     /* break: do tabs, or centering, and then dump the line out */
  1006.     beginline (FALSE);
  1007.     if (Alltabs > 0 || Ntabs > 0) /* some tabs have been set */
  1008.         {
  1009.         if ((lptr = dotab (lptr)) == NULL)
  1010.             {
  1011.             fprintf (stderr, "%s: malformed tab in %s at line %d\n",
  1012.                 Argv0, Filename, Outline);
  1013.             return (FAILURE);
  1014.             }
  1015.         }
  1016.     else if (Center == TRUE && *lptr != EOS && *lptr != EOL)
  1017.         repeat (SP, (Width - strlen (lptr)) / 2);
  1018.     while (*lptr != EOS && *lptr != EOL)
  1019.         putc (*lptr++, stdout);
  1020.     Curpos = Width; /* signal end of line */
  1021.     return (SUCCESS);
  1022.     }
  1023.  
  1024. /*FUNCTION dofill: do line filling */
  1025. void
  1026. dofill (lptr)
  1027. register    char    *lptr;
  1028.     {
  1029.     register char *eptr;       /* pointer to end of filled words */
  1030.     register int wordlen;      /* length of words */
  1031.  
  1032.     while (isspace (*lptr))
  1033.         lptr++;
  1034.     while (*lptr != EOS) /* fill text by picking up word by word */
  1035.         {
  1036.         eptr = lptr;
  1037.         while (*eptr != EOS && !isspace (*eptr))
  1038.             eptr++;
  1039.         wordlen = eptr - lptr;
  1040.         if ((Outline == 0) || (Curpos + wordlen) >= Width)
  1041.             beginline (TRUE);
  1042.         else if (Curpos < Width) /* space before word */
  1043.             {
  1044.             fillchar (SP);
  1045.             Curpos++;
  1046.             }
  1047.         for (Curpos += wordlen; lptr < eptr; lptr++)
  1048.             fillchar (*lptr);
  1049.         while (isspace (*lptr))
  1050.             lptr++;
  1051.  
  1052.         /* extra space at sentence ends (.?!) */
  1053.         if ((Curpos < Width) && isendsent (*(eptr-1)) && wordlen > 2)
  1054.             {
  1055.             fillchar (SP);
  1056.             Curpos++;
  1057.             }
  1058.         }
  1059.     }
  1060.  
  1061. /*FUNCTION ff: main formatting routine */
  1062. /*
  1063.     initializes for each new file, reads lines, trims space and blank lines
  1064.     switches between filling and non filling based on breakchars
  1065. */
  1066. Status
  1067. ff (file, ioptr)
  1068. char    *file;    /* file name */
  1069. FILE    *ioptr;   /* opened input pointer */
  1070.     {
  1071.     char    line[BUFSIZ];      /* lines read in here */
  1072.     register char *lptr;       /* pointer used to go through line */
  1073.     Status    status = SUCCESS;
  1074.  
  1075.     Outline = Pagenum = Pageline = Curpos = 0;
  1076.     Filename = file;
  1077.     Filling = FALSE;
  1078.  
  1079.     while (fgets (lptr = line, sizeof (line), ioptr))
  1080.         {
  1081.         if ((lptr = preprocess (lptr)) == NULL)
  1082.             continue;
  1083.  
  1084.         if ((Breaklines == TRUE)
  1085.         || (*lptr == EOL)
  1086.         || (Breakchars ? strchr (Breakchars, *lptr) != NULL : !isalnum (*lptr)))
  1087.             {
  1088.             if (dobreak (lptr) == FAILURE)
  1089.                 return (FAILURE);
  1090.             }
  1091.  
  1092.         else
  1093.  
  1094.             dofill (lptr);
  1095.         }
  1096.  
  1097.     Filling = FALSE;
  1098.     endpage (FALSE);
  1099.     return (status);
  1100.     }
  1101.